/*******************************************************************************
 *    版权信息   ： @copyright Copyright 2014-2021 chnenergy All Right Reserved
 *    作   者   ： hhz
 *    版   本   ： 1.1
 *    创建时间   ：2024/4/13
 *    最后修改日期   ： 2024/4/13
 *    最后修改人员   ： hhz
 ******************************************************************************/
package io.github.houhanzhi.hhregistry.service;

import io.github.houhanzhi.hhregistry.cluster.Snapshort;
import io.github.houhanzhi.hhregistry.model.InstanceMeta;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

/**
 * Default implementation of RegistryService
 **/
@Slf4j
public class HHRegistryService implements RegistryService {

    // 存取所有的 service 和对应的实例,可替换如下写法
//    final static Map<String, List<InstanceMeta>> REGISTRY = new HashMap();
    final static MultiValueMap<String, InstanceMeta> REGISTRY = new LinkedMultiValueMap<>();

    // 版本机制：客户端通过访问或者心跳拿到的版本跟本地服务版本对比来确定有没有变化
    final static Map<String, Long> VERSIONS = new ConcurrentHashMap<>();
    public final static AtomicLong VERSION = new AtomicLong(0);

    // 时间戳
    public final static Map<String, Long> TIMESTAMPS = new ConcurrentHashMap<>();


    @Override
    public synchronized InstanceMeta register(String service, InstanceMeta instance) {
        final List<InstanceMeta> metas = REGISTRY.get(service);
        if (!CollectionUtils.isEmpty(metas)) {
            if (metas.contains(instance)) {
                log.info(" ===> instance {} already registered", instance.toUrl());
                // 将该实例设置为可用
                instance.setStatus(true);
                return instance;
            }
        }
        log.info(" ===> register instance {}", instance.toUrl());
        REGISTRY.add(service, instance);
        instance.setStatus(true);
        renew(instance, service);
        // 服务注册，版本号+1
        VERSIONS.put(service, VERSION.incrementAndGet());
        return instance;
    }

    @Override
    public synchronized InstanceMeta unregister(String service, InstanceMeta instance) {

        final List<InstanceMeta> metas = REGISTRY.get(service);
        if (CollectionUtils.isEmpty(metas)) {
            return null;
        }

        log.info(" ===> unregister instance {}", instance.toUrl());
        metas.removeIf(m -> m.equals(instance));
        // 将该实例设置为不可用
        instance.setStatus(false);
        renew(instance, service);
        // 服务注册，版本号+1
        VERSIONS.put(service, VERSION.incrementAndGet());
        return instance;
    }

    @Override
    public List<InstanceMeta> getAllInstances(String service) {
        return REGISTRY.get(service);
    }

    @Override
    public synchronized long renew(InstanceMeta instance, String... services) {
        // 记录时间戳：根据时间戳探活上次的时间戳跟当前的时间戳对比，是否超过心跳检测的时间间隔(30s)
        // 探活不是针对 service，针对的是服务实例的 url
        final long now = System.currentTimeMillis();
        for (String service : services) {
            TIMESTAMPS.put(service + "@" + instance.toUrl(), now);
        }
        return now;
    }

    @Override
    public Long version(String service) {
        return VERSIONS.get(service);
    }

    @Override
    public Map<String, Long> versions(String... services) {
        return Arrays.stream(services)
                .collect(Collectors.toMap(k -> k, VERSIONS::get, (a, b) -> b));
    }


    /**
     * 深度拷贝生成一份新的快照
     *
     * @return
     */
    public static synchronized Snapshort snapshot() {
        LinkedMultiValueMap<String, InstanceMeta> registry = new LinkedMultiValueMap<>();
        registry.addAll(REGISTRY);
        Map<String, Long> versions = new HashMap<>(VERSIONS);
        Map<String, Long> timestamps = new HashMap<>(TIMESTAMPS);
        return new Snapshort(registry, versions, timestamps, VERSION.get());
    }

    /**
     * 恢复快照
     * @param snapshort
     * @return
     */
    public static synchronized long restore(Snapshort snapshort) {
        REGISTRY.clear();
        REGISTRY.addAll(snapshort.getREGISTRY());
        VERSIONS.clear();
        VERSIONS.putAll(snapshort.getVERSIONS());
        TIMESTAMPS.clear();
        TIMESTAMPS.putAll(snapshort.getTIMESTAMPS());
        VERSION.set(snapshort.getVersion());
        return snapshort.getVersion();
    }

}
